
#import "XmlParser.h"

@implementation XmlParser

//
// init
//
- (id)init
{
	//
	// call super
	//
	self																	= [super init];
	if(!self)
		return nil;
	
	_xmlParserContext														= 0;
	
	return self;
}

//
// close
//
- (void)close
{
	if(_xmlParserContext)
	{
		if(_xmlParserContext->myDoc)
			xmlFreeDoc(_xmlParserContext->myDoc);
		
		xmlFreeParserCtxt(_xmlParserContext);
	}
	
	_xmlParserContext														= 0;
	[_documentData release];
}

//
// dealloc
//
- (void)dealloc
{
	[self close];
	
	[super dealloc];
}

//
// parse html
//
- (BOOL)parseHtml
{
	if(!_documentData.length)
		return NO;
	
	_xmlParserContext														= htmlCreateMemoryParserCtxt((char const*)_documentData.bytes, _documentData.length);
	if(!_xmlParserContext)
		return NO;
	
	if(htmlCtxtUseOptions(_xmlParserContext, HTML_PARSE_RECOVER | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING))
		return NO;
	
	htmlParseDocument(_xmlParserContext);
	if(!_xmlParserContext->myDoc)
		return NO;
	
	return YES;
}

//
// parse xml
//
- (BOOL)parseXml
{
	if(!_documentData.length)
		return NO;
	
	static char zero														= 0;
	if(*((char const*)_documentData.bytes + _documentData.length - 1))
		[_documentData appendBytes:&zero length:sizeof(zero)];
	
	_xmlParserContext														= xmlCreateDocParserCtxt((xmlChar const*)_documentData.bytes);
	if(!_xmlParserContext)
		return NO;
	
	if(xmlParseDocument(_xmlParserContext) || !_xmlParserContext->myDoc)
		return NO;
	
	return YES;
}

//
// parse data
//
- (BOOL)parseDocumentData:(NSData*)documentData htmlMode:(BOOL)htmlMode
{
	[self close];
	_documentData															= [[NSMutableData alloc] initWithData:documentData];
	return htmlMode ? [self parseHtml] : [self parseXml];
}

//
// get nodes
//
- (NSArray*)nodesFromPath:(NSString*)nodesPath startNode:(xmlNodePtr)startNode
{
	if(!_xmlParserContext || !_xmlParserContext->myDoc)
		return nil;
	
	xmlXPathContextPtr xpathContext											= xmlXPathNewContext(_xmlParserContext->myDoc);
	if(!xpathContext)
		return nil;
	
	if(startNode)
		xpathContext->node													= startNode;
	
	NSMutableArray* retValue												= nil;
	xmlXPathObjectPtr xpathObject											= xmlXPathEvalExpression((xmlChar const*)[nodesPath UTF8String], xpathContext);
	if(xpathObject)
	{
		if(xpathObject->type == XPATH_NODESET && xpathObject->nodesetval)
		{
			retValue														= [NSMutableArray arrayWithCapacity:xpathObject->nodesetval->nodeNr];
			for(int i = 0; i < xpathObject->nodesetval->nodeNr; i ++)
				[retValue addObject:[NSValue valueWithPointer:xpathObject->nodesetval->nodeTab[i]]];
		}
		
		xmlXPathFreeObject(xpathObject);
	}
	
	xmlXPathFreeContext(xpathContext);
	return retValue;
}

//
// get node
//
- (xmlNodePtr)nodeFromPath:(NSString*)nodePath startNode:(xmlNodePtr)startNode
{
	if(!_xmlParserContext || !_xmlParserContext->myDoc)
		return nil;
	
	xmlXPathContextPtr xpathContext											= xmlXPathNewContext(_xmlParserContext->myDoc);
	if(!xpathContext)
		return nil;
	
	if(startNode)
		xpathContext->node													= startNode;
	
	xmlNodePtr retValue														= 0;
	xmlXPathObjectPtr xpathObject											= xmlXPathEvalExpression((xmlChar const*)[nodePath UTF8String], xpathContext);
	if(xpathObject)
	{
		if(xpathObject->type == XPATH_NODESET && xpathObject->nodesetval && xpathObject->nodesetval->nodeNr)
			retValue														= xpathObject->nodesetval->nodeTab[0];
		
		xmlXPathFreeObject(xpathObject);
	}
	
	xmlXPathFreeContext(xpathContext);
	return retValue;
}

//
// get string
//
- (NSString*)stringFromPath:(NSString*)valuePath startNode:(xmlNodePtr)startNode
{
	if(!_xmlParserContext || !_xmlParserContext->myDoc)
		return nil;
	
	xmlXPathContextPtr xpathContext											= xmlXPathNewContext(_xmlParserContext->myDoc);
	if(!xpathContext)
		return nil;
	
	if(startNode)
		xpathContext->node													= startNode;
	
	NSString* retValue														= nil;
	xmlXPathObjectPtr xpathObject											= xmlXPathEvalExpression((xmlChar const*)[valuePath UTF8String], xpathContext);
	if(xpathObject)
	{
		if(xpathObject->type == XPATH_NODESET && xpathObject->nodesetval && xpathObject->nodesetval->nodeNr)
		{
			xmlChar* value													= xmlXPathCastNodeSetToString(xpathObject->nodesetval);
			retValue														= [NSString stringWithCString:(char*)value encoding:NSUTF8StringEncoding];
			xmlFree(value);
		}
		
		xmlXPathFreeObject(xpathObject);
	}
	
	xmlXPathFreeContext(xpathContext);
	return retValue;
}

//
// get number
//
- (NSNumber*)doubleFromPath:(NSString*)valuePath startNode:(xmlNodePtr)startNode
{
	if(!_xmlParserContext || !_xmlParserContext->myDoc)
		return nil;
	
	xmlXPathContextPtr xpathContext											= xmlXPathNewContext(_xmlParserContext->myDoc);
	if(!xpathContext)
		return nil;
	
	if(startNode)
		xpathContext->node													= startNode;
	
	NSNumber* retValue														= nil;
	xmlXPathObjectPtr xpathObject											= xmlXPathEvalExpression((xmlChar const*)[valuePath UTF8String], xpathContext);
	if(xpathObject)
	{
		if(xpathObject->type == XPATH_NODESET && xpathObject->nodesetval && xpathObject->nodesetval->nodeNr)
			retValue														= [NSNumber numberWithDouble:xmlXPathCastNodeSetToNumber(xpathObject->nodesetval)];
		
		xmlXPathFreeObject(xpathObject);
	}
	
	xmlXPathFreeContext(xpathContext);
	return retValue;
}

//
// raw content
//
- (NSString*)rawContentFromPath:(NSString*)valuePath startNode:(xmlNodePtr)startNode
{
	if(!_xmlParserContext || !_xmlParserContext->myDoc)
		return nil;
	
	xmlXPathContextPtr xpathContext											= xmlXPathNewContext(_xmlParserContext->myDoc);
	if(!xpathContext)
		return nil;
	
	if(startNode)
		xpathContext->node													= startNode;
	
	NSString* retValue														= nil;
	xmlXPathObjectPtr xpathObject											= xmlXPathEvalExpression((xmlChar const*)[valuePath UTF8String], xpathContext);
	if(xpathObject)
	{
		if(xpathObject->type == XPATH_NODESET && xpathObject->nodesetval && xpathObject->nodesetval->nodeNr)
		{
			xmlBufferPtr nodeBuffer											= xmlBufferCreate();
			xmlNodeDump(nodeBuffer, _xmlParserContext->myDoc, xpathObject->nodesetval->nodeTab[0], 0, 1);
			if(nodeBuffer->content)
				retValue													= [NSString stringWithUTF8String:(char const*)nodeBuffer->content];
			xmlBufferFree(nodeBuffer);
		}
		
		xmlXPathFreeObject(xpathObject);
	}
	
	xmlXPathFreeContext(xpathContext);
	return retValue;
}

@end
